package com.zkingsoft.common.authority;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.matrix.core.constance.MatrixConstance;
import com.matrix.core.tools.StringUtils;
import com.matrix.core.tools.WebUtil;
import com.zkingsoft.common.authority.strategy.LoginStrategy;
import com.zkingsoft.common.bean.SysFnBtnRel;
import com.zkingsoft.common.bean.SysFunction;
import com.zkingsoft.common.bean.SysUsers;
import com.zkingsoft.common.constance.AppConstance;
import com.zkingsoft.common.service.SysFunctionService;

/**
 * DefaultAuthorityManager 实现了权限控制接口
 * 
 * @author JIANGYOUYAO
 * @email 935090232@qq.com
 * @date 2017年12月6日
 */
@Component("defaultAuthorityManager")
public class DefaultAuthorityManager implements AuthorityManager {

	@Autowired
	SysFunctionService sysFunctionService;

	public static final String USERFUNCTION = "userFunction";
	public static final String MENUSFUNCTION = "menusFunction";
	/** 用户所有路径权限的记录 **/
	public static final String USER_URL_MAPPING = "userUrlMapping";

	private DefaultAuthorityManager() {
	}

	public <T> T login(LoginStrategy loginStrategy) {
		@SuppressWarnings("unchecked")
		T user = (T) loginStrategy.login();
		WebUtil.setSessionAttribute(MatrixConstance.LOGIN_KEY, user);
		WebUtil.setSessionAttribute(AppConstance.DEFAULT_AUTHORITYMANAGER, this);
		return user;
	}

	@Override
	public void getLoginOut() {
		WebUtil.getSession().removeAttribute(MatrixConstance.LOGIN_KEY);
	}

	/**
	 * 判断用户是否具有按钮功能
	 * 
	 * @param matchStr
	 * @return
	 * @throws IllegalArgumentException
	 */
	@Override
	public boolean isBtnPermitted(String matchStr) {
		// 开发人员和超级管理员具有所有权限
		SysUsers user = WebUtil.getSessionAttribute(MatrixConstance.LOGIN_KEY);

		Map<String, SysFunction> userFunction = WebUtil.getSessionAttribute(USERFUNCTION);
		// 企业管理员不校验按钮权限
		if (AppConstance.USER_TYPE_DEVELOPER.equals(user.getSuUserType())
				|| AppConstance.USER_TYPE_SUPER.equals(user.getSuUserType())
				|| AppConstance.USER_TYPE_ADMIN.equals(user.getSuUserType())) {
			return true;
		}
		String[] strs = matchStr.split(":");
		if (strs.length != 2) {
			throw new IllegalArgumentException("权限matchStr格式错误，需要fnCode:btnValue");
		}

		SysFunction fn = userFunction.get(strs[0].trim());
		// 功能是否存在
		if (fn == null) {
			return false;
		} else {
			return StringUtils.isContentSet(strs[1].trim(), fn.getRpfBns());
		}
	}

	/**
	 * 判断用户是否具有功能权限
	 * 
	 * @param matchStr
	 * @return
	 */
	@Override
	public boolean isFnPermitted(String fnCode) {
		// 开发人员和超级管理员具有所有权限
		SysUsers user = WebUtil.getSessionAttribute(MatrixConstance.LOGIN_KEY);
		if (AppConstance.USER_TYPE_DEVELOPER.equals(user.getSuUserType())
				|| AppConstance.USER_TYPE_SUPER.equals(user.getSuUserType())) {
			return true;
		}
		Map<String, SysFunction> userFunction = WebUtil.getSessionAttribute(USERFUNCTION);
		SysFunction fn = userFunction.get(fnCode);
		return fn == null ? false : true;
	}

	/**
	 * 初始化用户权限
	 * 
	 * @author JIANGYOUYAO
	 * @email 935090232@qq.com
	 * @date 2017年12月5日
	 */
	public void initUserPower() {

		// 记录用户所有的功能
		Map<String, SysFunction> userFunction = new HashMap<>(MatrixConstance.COLLECTION_SIZE);

		// 用户的一级菜单权限
		List<SysFunction> menuFunction = new ArrayList<>();

		//
		List<String> userUrlMapping = new ArrayList<>();

		// 用户的所有功能权限用id记录，方便后面查询菜单树形结构
		Map<String, SysFunction> menuFunctionMap = new TreeMap<>();

		// 获取用户所有权限
		getUserFunction(userFunction, menuFunctionMap, userUrlMapping);

		// 组装菜单
		assembleMenu(menuFunction, menuFunctionMap);

		// 把用户菜单和用户的功能都存在session中。
		WebUtil.setSessionAttribute(USERFUNCTION, userFunction);
		WebUtil.setSessionAttribute(MENUSFUNCTION, menuFunction);
		WebUtil.setSessionAttribute(USER_URL_MAPPING, userUrlMapping);

	}

	/**
	 * 获取用的功能，包括菜单功能和非菜单功能
	 * 
	 * @author JIANGYOUYAO
	 * @email 935090232@qq.com
	 * @date 2017年12月5日
	 * @param userFunctionMap
	 * @param menuFunctionMap
	 * @param userUrlMapping
	 */
	private void getUserFunction(Map<String, SysFunction> userFunctionMap, Map<String, SysFunction> menuFunctionMap,
			List<String> userUrlMapping) {
		SysUsers sysUser = WebUtil.getSessionAttribute(MatrixConstance.LOGIN_KEY);
		// 判断用户类型
		if (AppConstance.USER_TYPE_ADMIN.equals(sysUser.getSuUserType())) {
			// 管理员拥有公司全部权限
			List<SysFunction> tempList = sysFunctionService.findCompanyFunction(sysUser.getCompanyId());
			// 区分菜单和非菜单功能
			for (SysFunction sysFunction : tempList) {

				userFunctionMap.put(sysFunction.getFnCode(), sysFunction);
				// 注册访问路径
				registerUrlMapping(userUrlMapping, sysFunction);

				// 如果是菜单功能单独记录
				if (AppConstance.IS_Y.equals(sysFunction.getFnShowMenu())) {
					menuFunctionMap.put(sysFunction.getFnId(), sysFunction);
				}
			}
		} else if (AppConstance.USER_TYPE_EMPLOYEE.equals(sysUser.getSuUserType())) {
			// 普通员工账号只拥有自己所拥有的权限
			List<SysFunction> userFunctionList = sysFunctionService.findFunctionByRoleIds(sysUser.getRoleIds());
			for (SysFunction sysFunction : userFunctionList) {
				// TODO注册访问路径
				registerUrlMapping(userUrlMapping, sysFunction);
				if (userFunctionMap.containsKey(sysFunction.getFnCode())) {
					// 如果功能已经被添加到集合中则追加权限按钮
					SysFunction oneFunctionInMap = userFunctionMap.get(sysFunction.getFnCode());
					// 为了方便判断所以用字符串记录一下
					if (StringUtils.isBlank(oneFunctionInMap.getRpfBns())) {
						oneFunctionInMap.setRpfBns(sysFunction.getRpfBns());
					} else {
						oneFunctionInMap.setRpfBns(oneFunctionInMap.getRpfBns() + "," + sysFunction.getRpfBns());
					}
				} else {
					// 如果是新功能则直接添加
					userFunctionMap.put(sysFunction.getFnCode(), sysFunction);
					if (AppConstance.IS_Y.equals(sysFunction.getFnShowMenu())) {
						menuFunctionMap.put(sysFunction.getFnId(), sysFunction);
					}
				}
			}
		}
	}

	/**
	 * 注册功能和按钮的访问路径
	 * 
	 * @author JIANGYOUYAO
	 * @email 935090232@qq.com
	 * @date 2017年12月8日
	 * @param userUrlMapping
	 * @param sysFunction
	 */
	private void registerUrlMapping(List<String> userUrlMapping, SysFunction sysFunction) {
		String path = sysFunction.getFnPath();
		if (StringUtils.isNotBlank(path) && !userUrlMapping.contains(path)) {
			userUrlMapping.add(path);
		}
		// 注册按钮路径
		List<SysFnBtnRel> btnRels = sysFunction.getSysFnBtnRel();
		if (CollectionUtils.isNotEmpty(btnRels)) {
			for (SysFnBtnRel sysFnBtnRel : btnRels) {
				String btnPath = sysFnBtnRel.getFbPath();
				if (StringUtils.isNotBlank(btnPath) && !userUrlMapping.contains(btnPath)) {
					userUrlMapping.add(btnPath);
				}
			}
		}

	}

	/**
	 * 把菜单组装成树形结构
	 * 
	 * @author JIANGYOUYAO
	 * @email 935090232@qq.com
	 * @date 2017年12月5日
	 * @param menuFunction
	 * @param menuFunctionMap
	 */
	private void assembleMenu(List<SysFunction> menuFunction, Map<String, SysFunction> menuFunctionMap) {
		// 将map.entrySet()转换成list,并按照功能的FnSequence倒序
		List<Entry<String, SysFunction>> list = new ArrayList<>(menuFunctionMap.entrySet());
		Collections.sort(list, new Comparator<Entry<String, SysFunction>>() {
			@Override
			public int compare(Entry<String, SysFunction> o1, Entry<String, SysFunction> o2) {
				Integer a = o1.getValue().getFnSequence() == null ? 0 : o1.getValue().getFnSequence();
				Integer b = o2.getValue().getFnSequence() == null ? 0 : o2.getValue().getFnSequence();
				return Integer.compare(b, a);
			}
		});
		// 获取菜单权限,思路：如果遍历map如果功能存在父节点，就找到父节点然后把子节点加入进去，这样就只需要遍历一次
		for (Entry<String, SysFunction> entry : list) {
			String id = entry.getKey();

			SysFunction function = menuFunctionMap.get(id);
			// 如果是一级节点则直接存入菜单
			if (StringUtils.isBlank(function.getFnParentId())) {
				menuFunction.add(function);
			} else {
				// 非一级节点找到父节点后存入
				SysFunction parentFn = menuFunctionMap.get(function.getFnParentId());
				List<SysFunction> childs = parentFn.getChilds();
				if (childs == null) {
					parentFn.setChilds(new ArrayList<SysFunction>());
				}
				parentFn.getChilds().add(function);
			}
		}
	}

}
